﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using CLUBsInterpreter.ObjectTypes;

namespace CLUBsInterpreter.AST
{
	/**
	 * <summary>
	 * Describes a composite identifier of an object in the CLUBs environment.
	 * </summary>
	 **/
	class Identifier : AbstractSyntaxTree
	{

		private Scope m_container;

		private bool m_isReference = true;
		private CLUBsObject m_reference = new CLUBsNull();

		// Tree Structure
		/**
		 * <summary>
		 * The individual array identifiers in this composite identifier.
		 * </summary>
		 **/
		public List<ArrayIdentifier> SubIdents = new List<ArrayIdentifier>();
		// Tree Decoration
		public uint LineNumber;

		/**
		 * <summary>
		 * The inferred type of the object that this identifier points to.
		 * </summary>
		 **/
		public IdentifierType InferredType;
		/**
		 * <summary>
		 * The object that this identifier points to.
		 * </summary>
		 **/
		public CLUBsObject Reference
		{
			get
			{

				/*if (m_isReference)
					return Container.LocalVariables[SubIdents.Last().Name];
				else
				{*/
					// Go through ArrayIdents, searching the scopes
					CLUBsObject obj = null;
					
					foreach (ArrayIdentifier ai in SubIdents)
					{
						if (ai.LastArrayIdentifier == null)
						{
							obj = ai.FromScope(ContainingScope);
						}
						else
						{
							if (obj.ObjectIdentifierType == IdentifierType.Cell)
								obj = ai.FromScope(((CLUBsCell)obj).LocalVariables);
							else
								obj = ai.FromScope(obj.LocalVariables);
						}
					}


					if (obj != null)
						return obj;
					else
						return new CLUBsNull();
				//}
			}
			set
			{
				if (m_isReference)
				{
					// Go through ArrayIdents, searching the scopes
					CLUBsObject obj = null;

					if (SubIdents.Count > 1)
					{
						obj = SubIdents[0].FromScope(ContainingScope);

						for (int i = 1; i < SubIdents.Count - 1; i++)
						{
							ArrayIdentifier ai = SubIdents[i];

							if (obj.ObjectIdentifierType == IdentifierType.Cell)
								obj = ai.FromScope(((CLUBsCell)obj).LocalVariables);
							else
								obj = ai.FromScope(obj.LocalVariables);
						}
						if (obj != null)
							obj.LocalVariables[SubIdents.Last().Name] = value;
					}
					else
					{
						ContainingScope[SubIdents.Last().Name] = value;
					}
				}
				else
				{
					ContainingScope.Declare(SubIdents.Last().Name, value);
				}
			}
		}

		public Scope ContainingScope
		{
			get
			{
				if (m_container == null)
					m_container = StandardEnvironment.Instance.CurrentBlock.InternalScope;
				return m_container;
			}
		}


		public void SetDeclaration(bool set = true)
		{
			m_isReference = !set;
		}

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitIdentifierSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitIdentifier(this);
			}
		}

		public override string ToString()
		{
			// Initialize empty string
			string ret = "";
			// Add identifier and separator for each ArrayIdentifier component
			foreach (ArrayIdentifier ident in SubIdents)
			{
				ret += ident + "->";
			}
			// If we actually added any identifiers, remove the trailing separator
			if (ret.Length > 0) ret = ret.Remove(ret.Length - 2);
			// Return result
			return ret;
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the identifier node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "Identifier (composite)");
			// Print each ArrayIdentifier component
			foreach (ArrayIdentifier ident in SubIdents)
			{
				ident.PrintTree(outStream, depth + 1);
			}
		}
	}

	/**
	 * <summary>
	 * Describes an array identifier (typically part of a composite identifier) in the CLUBs environment.
	 * </summary>
	 **/
	class ArrayIdentifier : AbstractSyntaxTree
	{
		// Tree Decoration
		/**
		 * <summary>
		 * The name of the object/array.
		 * </summary>
		 **/
		public string Name;
		public uint LineNumber;
		/**
		 * <summary>
		 * The array indices supplied.
		 * WARNING: CLUBs array functionality is NOT IMPLEMENTED,
		 * and is not MEANT to be implemented at this stage of development!
		 * </summary>
		 **/
		public List<int> Indices = new List<int>();
		/**
		 * <summary>
		 * The inferred type of the object that this identifier points to.
		 * </summary>
		 **/
		public IdentifierType InferredType;
		/**
		 * <summary>
		 * The object that this identifier points to.
		 * </summary>
		 **/
		public CLUBsObject Reference;
		/**
		 * <summary>
		 * When evaluating a composite idenfitier, this will point to the previous array identifier,
		 * or null if this is the first array identifier in the composite.
		 * </summary>
		 **/
		public ArrayIdentifier LastArrayIdentifier;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitArrayIdentifierSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitArrayIdentifier(this);
			}
		}

		public override string ToString()
		{
			// Initialize string with identifier name
			string ret = Name;
			// For each index, add it to the string
			foreach (int index in Indices)
			{
				ret += "["+index+"]";
			}
			// Return result
			return ret;
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the array identifier node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "ArrayIdentifier ("+Indices.Count+" indices)");
		}

		public CLUBsObject FromScope(Scope scope)
		{
			return scope[Name];
		}
	}
}
